<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ECMAScript 修改对象</title>
    <script>
        /*
        Js  中函数本身也是对象  这点和 Java 等还是有很大的区别 
        还可以修改已有对象的行为。
        prototype 属性不仅可以定义构造函数的属性和方法，还可以为本地对象添加属性和方法
        */ 
        Number.prototype.toHexString = function (){
            return this.toString(16);
        }

        //  演示了 通过已有的方法创建新方法
        function testNumberToString(){
            var iNum = 15;
            alert(iNum.toHexString()); // 输出 "F"
        }

        //  新增 更通俗易懂的方法
        Array.prototype.enqueue = function(vItem) {
            this.push(vItem);
        };

        Array.prototype.dequeue = function() {
            return this.shift();
        };

    
        function testAddMethod(){

            var arr = new Array(3)
            arr[0] = "George"
            arr[1] = "John"
            arr[2] = "Thomas"

            arr.enqueue("Bill");
            document.write(arr);

            document.write("<br />")

            arr.dequeue();
            document.write(arr);
        }

        /*
        添加与已有方法无关的方法 
        假设要判断某个项在数组中的位置，没有本地方法可以做这种事情。
        我们可以轻松地创建下面的方法：
        */
        Array.prototype.indexOf = function(vItem){
            for(var i =0; i < this.length;i++){  // 
                if (vItem == this[i]){
                    return i
                }
            }
            return -1;
        }
        
        function testArrayIndex(){
            var aColors = new Array("red","green","blue");
            alert(aColors.indexOf("green"));	//输出 "1"
        }

        /*
        为本地对象添加新方法 
        */
        Object.prototype.showValue = function () {
            alert(this.valueOf());
        };

        function testObjShowValue(){
            var str = "hello";
            var iNum = 25;
            str.showValue();		//输出 "hello"
            iNum.showValue();		//输出 "25"    
            // 为 Object 添加的 方法可以试用于  String  Number 等类型
        }

        /*
        重定义已有方法 相当于重写方法
        就像能给已有的类定义新方法一样，也可重定义已有的方法。如前面的章节所述，
        函数名只是指向函数的指针，因此可以轻松地指向其他函数。如果修改了本地方法，如 toString()，
        会出现什么情况呢？
        自定义的 function 都是 Function 类的方法 
        */
        Function.prototype.toString = function() {
            return "Function code hidden";
        }

        function sayHi(){
            alert('say hi')
        }

        function testSayHi(){
            alert(sayHi.toString())   // 但是 这样的缺点是所有的函数的toString 都变成了我们改成的这个 非常危险 ！！
            alert(testObjShowValue.toString())  // 这样会产生问题
        }

        /*
        改进的方法是 
        没有能够恢复原始函数的方法，所以在覆盖原始方法前，比较安全的做法是存储它的指针，以便以后的使用。
        有时你甚至可能在新方法中调用原始方法：
        */
        Function.prototype.originalToString = Function.prototype.toString;

        Function.prototype.toString = function() {
            if (this.originalToString().length > 100) {
                return "Function too long to display.";
            } else {
                return this.originalToString();
            }
        };

        function testNewAddMethod (){
            alert(sayHi.toString())   
            alert(testObjShowValue.toString())  
        }

        /*
        下面介绍一下极晚绑定 
        从技术上讲，根本不存在极晚绑定。本书采用该术语描述 ECMAScript 中的一种现象，
        即能够在对象实例化后再定义它的方法。例如：
        在大多数程序设计语言中，必须在实例化对象之前定义对象的方法。这里，方法 sayHi() 
        是在创建 Object 类的一个实例之后来添加进来的。在传统语言中不仅没听说过这种操作，
        也没听说过该方法还会自动赋予 Object 对象的实例并能立即使用（接下来的一行）。
        注意：不建议使用极晚绑定方法，因为很难对其跟踪和记录。不过，还是应该了解这种可能。
        纠正一下： 其他编程语言还是可以的 
        */
        function testVeryLateBinding(){
            var o = new Object();

            Object.prototype.sayHi = function () {
            alert("hi");
            };

            o.sayHi();
        }

    </script>
</head>
<body>
    <button onclick="testNumberToString()">testNumberToString</button>
    <button onclick="testAddMethod()">testAddMethod</button>   
    <button onclick="testArrayIndex()">testArrayIndex</button>   
    <button onclick="testSayHi()">testSayHi</button> 
    <button onclick="testNewAddMethod()">testSayHi</button>
    <button onclick="testVeryLateBinding()">testSayHi</button>   
</body>
</html>